#include "Logging.h"

#include "../thread/CurrentThread.h"

#include <cerrno>
#include <cstdio>
#include <cstring>
#include <unistd.h>

__thread char t_errnobuf[512];
__thread char t_time[64];
__thread time_t t_lastSecond;

const char *strerror_tl(int savedErrno) {
    return strerror_r(savedErrno, t_errnobuf, sizeof t_errnobuf);
}

Logger::LogLevel initLogLevel() {
    if (::getenv("MUDUO_LOG_TRACE"))
        return Logger::TRACE;
    else if (::getenv("MUDUO_LOG_DEBUG"))
        return Logger::DEBUG;
    else
        return Logger::INFO;
}

Logger::LogLevel g_logLevel = initLogLevel();

const char *LogLevelName[Logger::NUM_LOG_LEVELS] =
        {
                "TRACE ",
                "DEBUG ",
                "INFO  ",
                "WARN  ",
                "ERROR ",
                "FATAL ",
        };

// helper class for known string length at compile time
class T {
public:
    T(const char *str, unsigned len)
            : str_(str),
              len_(len) {
        assert(strlen(str) == len_);
    }

    const char *str_;
    const unsigned len_;
};

inline LogStream &operator<<(LogStream &s, T v) {
    s.append(v.str_, v.len_);
    return s;
}

inline LogStream &operator<<(LogStream &s, const Logger::SourceFile &v) {
    s.append(v.data_, v.size_);
    return s;
}

//TODO :默认的追加和写入函数
void defaultOutput(const char *msg, int len) {
    // size_t n = fwrite(msg, 1, len, stdout);
    size_t n = fwrite(msg, 1, len, stderr);
    //FIXME check n
    (void) n;
}

void defaultFlush() {
    fflush(stderr);
}

/* 日志对应的输出和保存函数 */
Logger::OutputFunc g_output = defaultOutput;
Logger::FlushFunc g_flush = defaultFlush;
bool g_isAsync = false;
// TimeZone g_logTimeZone;


Logger::Impl::Impl(LogLevel level, int savedErrno, const SourceFile &file, int line)
        : time_(Timestamp::now()),
          stream_(),
          level_(level),
          line_(line),
          basename_(file) {
    formatTime();
    // 输入基本的时间信息
    CurrentThread::getTid();
    stream_ << T(CurrentThread::tidString(), CurrentThread::tidStringLength());
    // 获取相乘的tid输入
    stream_ << T(LogLevelName[level], 6);
    // 获取日志名称
    if (savedErrno != 0) {
        stream_ << strerror_tl(savedErrno) << " (errno=" << savedErrno << ") ";
        // 读取错误原因输出
    }
}

void Logger::Impl::formatTime() {
    int64_t microSecondsSinceEpoch = time_.microSecondsSinceEpoch();
    time_t seconds = static_cast<time_t>(microSecondsSinceEpoch / Timestamp::kMicroSecondsPerSecond);
    int microseconds = static_cast<int>(microSecondsSinceEpoch % Timestamp::kMicroSecondsPerSecond);
    if (seconds != t_lastSecond) {
        t_lastSecond = seconds;
        struct tm tm_time;
        /*  if (g_logTimeZone.valid()) {
              tm_time = g_logTimeZone.toLocalTime(seconds);
          } else {
              ::gmtime_r(&seconds, &tm_time); // FIXME TimeZone::fromUtcTime
          }*/
        ::gmtime_r(&seconds, &tm_time);

        int len = snprintf(t_time, sizeof(t_time), "%4d-%02d-%02d %02d:%02d:%02d",
                           tm_time.tm_year + 1900, tm_time.tm_mon + 1, tm_time.tm_mday,
                           tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec);
        assert(len == 19);
        (void) len;
    }

    /* if (g_logTimeZone.valid()) {
         Fmt us(".%06d ", microseconds);
         assert(us.length() == 8);
         stream_ << T(t_time, 17) << T(us.data(), 8);
     } else {
         Fmt us(".%06dZ ", microseconds);
         assert(us.length() == 9);
         stream_ << T(t_time, 17) << T(us.data(), 9);
     }*/
    Fmt us(".%06dZ ", microseconds);
    assert(us.length() == 9);
    stream_ << T(t_time, 19) << T(us.data(), 9);
}


void Logger::Impl::finish() {
    stream_ << " - [" << basename_ << ':' << line_ << "]\n";
}

/* 下面是Logger的初始化过程*/
Logger::Logger(SourceFile file, int line)
        : impl_(INFO, 0, file, line) {
    // 初始化的时候初始化普通的impl
}

Logger::Logger(SourceFile file, int line, LogLevel level, const char *func)
        : impl_(level, 0, file, line) {
    impl_.stream_ << func << ' ';
}

Logger::Logger(SourceFile file, int line, LogLevel level)
        : impl_(level, 0, file, line) {
}

Logger::Logger(SourceFile file, int line, bool toAbort)
        : impl_(toAbort ? FATAL : ERROR, errno, file, line) {
}

/**
 * Logger的析构函数, 会在析构的时候调用output将日志信息通过对应的输出函数保存起来
 * 这里的追加函数是外部定义的, 将日志的输出和日志的记录进行了解耦操作
 */
Logger::~Logger() {
    impl_.finish();
    const LogStream::Buffer &buf(stream().buffer());
    g_output(buf.data(), buf.length());
    // 将日志输出
    if (impl_.level_ == FATAL) {
        // 日志等级位FATAL的话就直接输出日志并退出程序
        // 实际输入的调用时机要看Log的设定
        g_flush();
        // 当使用AsyncLogging的时候, 因为没有定义g_flush, 所以这里会直接执行一个空操作后退出
        if (g_isAsync) {
            sleep(3);
            // FIXME : 让自己睡眠3s, 保证最后可以及时将数据写入, 当没有使用异步线程作为日志的时候要注释掉该选项
        }
        abort();
    }
}

void Logger::setLogLevel(Logger::LogLevel level) {
    g_logLevel = level;
}

void Logger::setOutput(OutputFunc out) {
    g_output = out;
}

void Logger::setFlush(FlushFunc flush) {
    g_flush = flush;
}

// FIXME : 如果有更好的选项将这里替换为其他的方式
void Logger::setIsAsync(bool isAsync) {
    g_isAsync = isAsync;
}

/*
void Logger::setTimeZone(const TimeZone &tz) {
    g_logTimeZone = tz;
}
*/
